﻿
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace NetCore.ORM
{
    /// <summary>
    /// Entity属性列表 用于缓存
    /// </summary>
    public class DbEntityModel
    {
        public TableAttribute Table { get; set; }

        public string TableName
        {
            get
            {
                if (Table != null)
                {
                    return Table.Name;
                }
                return "";
            }
        }

        public List<DbColumnAttribute> DbFields { get; set; }
        [JsonIgnore]
        public List<PropertyInfo> EntityFields { get; set; }

        public bool Identifier { get; set; }

      
        /// <summary>
        /// 获取主键
        /// </summary>
        public string PrimaryKey { get; set; }
       
        /// <summary>
        /// sql字段
        /// </summary>
        public string StrFieldBuilder { get; set; }

       

        public DbEntityModel(Type t)
        {
            var entityFields = new List<PropertyInfo>();
            this.DbFields = ReflectionHelper.getDbColumnProperty(t, ref entityFields);
            this.EntityFields = entityFields;
            if (entityFields == null || entityFields.Count <= 0)
            {
                this.EntityFields = ReflectionHelper.GetProperties(t).ToList();
            }
            SetTableInfo(t);
            SetField();
            SetPrimary();
        }

        private void SetTableInfo(Type t)
        {
            this.Table = (TableAttribute)t.GetCustomAttributes(typeof(TableAttribute), true).FirstOrDefault();

            if (this.DbFields != null && this.DbFields.Count > 0)
            {
                int index = 0;
                if (this.Table != null)
                {
                    if (!Table.IsCreated)
                    {

                        index = this.DbFields.FindIndex(x => x.Name.ToLower() == EntityBase.__CreateBy.ToLower());
                        if (index >= 0)
                        {
                            this.DbFields.RemoveAt(index);
                        }
                        index = this.DbFields.FindIndex(x => x.Name.ToLower() == EntityBase.__CreateDate.ToLower());
                        if (index >= 0)
                        {
                            this.DbFields.RemoveAt(index);
                        }

                    }
                    if (!Table.IsLastModify)
                    {
                        index = this.DbFields.FindIndex(x => x.Name.ToLower() == EntityBase.__LastModifyDate.ToLower());
                        if (index >= 0)
                        {
                            this.DbFields.RemoveAt(index);
                        }
                        index = this.DbFields.FindIndex(x => x.Name.ToLower() == EntityBase.__LastModifyUserId.ToLower());
                        if (index >= 0)
                        {
                            this.DbFields.RemoveAt(index);
                        }
                    }
                    if (!Table.IsDeleted)
                    {
                        index = this.DbFields.FindIndex(x => x.Name.ToLower() == EntityBase.__IsDeleted.ToLower());
                        if (index >= 0)
                        {
                            this.DbFields.RemoveAt(index);
                        }

                    }
                }
            }
        }

        private void SetField()
        {
            if (this.DbFields == null || this.DbFields.Count <= 0)
            {
                return;
            }
           int index = 0;
            foreach (var field in this.DbFields)
            {
                if (field.Ignore || !field.IsEnabled)
                    continue;

                if (!string.IsNullOrWhiteSpace(field.Name))
                {
                    if (index >= 1)
                    {
                        StrFieldBuilder += ",";
                    }
                    index++;
                    StrFieldBuilder += field.Name;
                }

            }
        }

        private void SetPrimary()
        {
            if (DbFields == null || DbFields.Count <= 0)
            {
                throw new Exception("参数错误");
            }

            if (string.IsNullOrWhiteSpace(PrimaryKey))
            {
                var primarykey = DbFields.Where(x => x.IsPrimaryKey).FirstOrDefault();
                if (primarykey != null)
                {
                    Identifier = primarykey.Identifier;
                    PrimaryKey = primarykey.Name;
                }
                else
                {
                    throw new Exception("该类不包含主键" + TableName);
                }
            }
        }
    }

    /// <summary>
    /// 实体操作类
    /// </summary>
    public class DbFactoryHelper
    {
       
         private DbEntityModel _entitymap;

        
         public DbEntityModel EntityMap
         {
             get {
                 return _entitymap;
             }
         }
         public Type entityObj;
         public DbFactoryHelper(Type t)
         {
             entityObj = t;
            _entitymap = new DbEntityModel(t);
            _entitymap.EntityFields = t.GetProperties().ToList();
         }
        public bool IsUpdate(object entity)
        {
            if (entity is EntityBase)
            {
               return (entity as EntityBase).IsUpdated;
            }
            return false;
        }

        #region 实体绑定
        /// <summary>
        /// 删除缓存中的实体
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        public bool RemoveCacheEntity(Type type,object[] ids)
        {
            if (ids!=null&& ids.Length>0)
            {
                foreach (var id in ids)
                {
                    AppHttpContext.CacheService.RemoveCache(this.GetEntityCacheKey(id.ToString()));
                }
            }
            return true;
        }

        public bool RemoveCacheEntity<T>(T entity)
        {
            string id = this.GetPrimaryId(entity).ToString();
            if (!string.IsNullOrWhiteSpace(id))
            {
                AppHttpContext.CacheService.RemoveCache(this.GetEntityCacheKey(id));
            }
            return true;
        }

        public void CacheMerge<T>(object entity)where T:class, new()
        {
            string id = this.GetPrimaryId(entity).ToString();
            string cacheId = GetEntityCacheKey(id);
            T oldEntity = AppHttpContext.CacheService.GetCache<T>(cacheId);
            if (oldEntity != null)
            {
                if (entity !=null)
                {
                    var newEntity = entity as EntityBase;
                    if (newEntity != null && newEntity.UpdateField != null && newEntity.UpdateField.Count > 0)
                    {
                        foreach (var field in newEntity.UpdateField)
                        {
                            var attr = EntityMap.EntityFields.Where(x => x.Name.ToLower().Equals(field)).FirstOrDefault();
                            if (attr != null)
                            {
                                attr.SetValue(oldEntity, attr.GetValue(newEntity, null),null);
                            }
                        }
                    }
                    AppHttpContext.CacheService.Add(GetEntityCacheKey(id), entity, TimeSpan.FromMinutes(EntityMap.Table.CacheTime));
                }

            }
            else
            {
                if (entity != null)
                {
                    AppHttpContext.CacheService.Add(GetEntityCacheKey(id), entity, TimeSpan.FromMinutes(EntityMap.Table.CacheTime));
                }
            }
        }
        /// <summary>
        /// 获取实体缓存的Key值
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public string GetEntityCacheKey(string id)
        {
            return (ConfigHelper.Cache_Entity_Key + EntityMap.Table.CacheKey + "_" + id).ToUpper();
        }

        /// <summary>
        /// 缓存获取
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public object GetCache(string id)
        {
            return AppHttpContext.CacheService.GetCache(this.GetEntityCacheKey(id));
        }
        /// <summary>
        /// 缓存添加
        /// </summary>
        /// <param name="id"></param>
        /// <param name="entity"></param>
        /// <returns></returns>
        public object SetCache(string id,object entity)
        {
            return AppHttpContext.CacheService.Add(this.GetEntityCacheKey(id), entity);
        }

        #endregion

        
        
        public object GetPrimaryId(object entity)
        {
            if (entity == null)
            {
                return null;
            }
            var attr = EntityMap.EntityFields.Where(x => x.Name.Equals(EntityMap.PrimaryKey)).FirstOrDefault();
            if (attr != null)
            {
                return attr.GetValue(entity);
            }
            return null;
        }

        public void SetValue(object entity, string fieldName, object value)
        {
            if (entity == null)
            {
                return;
            }
            var attr = EntityMap.EntityFields.Where(x => x.Name.Equals(fieldName)).FirstOrDefault();
            if (attr != null&& attr.SetMethod!=null)
            {
                attr.SetValue(entity, value);
            }

        }
        public object GetValue(object entity, PropertyInfo attr)
        {
            if (entity == null)
            {
                return null;
            }

            if (attr != null)
            {
                return attr.GetValue(entity);
            }
            return null;
        }

        #region 实体绑定
        /// <summary>
        /// 单个实体绑定，启用缓存
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="reader"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public T DataReaderToEntity<T>( IDataReader reader, string id) where T : new()
        {
            return DataReaderToEntity<T>( reader);

        }
        public T DataReaderToEntity<T>(IDataReader reader) where T : new()
        {
            T entity = new T();
            if (reader != null)
            {
                if (entity is Dictionary<string, object>)
                {
                    var obj = entity as Dictionary<string, object>;
                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        var value = reader.GetValue(i);
                        if (value != DBNull.Value)
                        {
                            obj.Add(reader.GetName(i), value);
                        }
                    }
                }
                else
                {
                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        var attr = this.EntityMap.EntityFields.Where(x => x.Name == reader.GetName(i)).FirstOrDefault();
                        if (attr != null)
                        {
                            var value = reader.GetValue(i);
                            if (value != DBNull.Value)
                            {
                                attr.SetValue(entity, value);
                            }
                        }
                    }
                }
            }
            if (entity != null && entity is EntityBase)
            {
                var entityClass = (entity as EntityBase);
                entityClass.IsUpdated = true;
                entityClass.UpdateField.Clear();
            }
            return entity;
        }
        public object DataReaderToEntity(DbEntityModel entitymap, IDataReader reader, object entity)
        {
            if (reader != null)
            {
                for (int i = 0; i < reader.FieldCount; i++)
                {
                    var attr = this.EntityMap.EntityFields.Where(x => x.Name == reader.GetName(i)).FirstOrDefault();
                    if (attr != null)
                    {
                        var value = reader.GetValue(i);
                        if (value != DBNull.Value)
                        {
                            attr.SetValue(entity, value);
                        }
                    }
                }

            }
            if (entity != null && entity is EntityBase)
            {
                var entityClass = (entity as EntityBase);
                entityClass.IsUpdated = true;
                entityClass.UpdateField.Clear();
            }
            return entity;
        }
        public T DataRowToEntity<T>( DataRow row, string id) where T : new()
        {
            return DataRowToEntity<T>( row);
        }

        public T DataRowToEntity<T>(DataRow row) where T : new()
        {
            T entity = new T();
            if (row != null)
            {
                if (entity is Dictionary<string, object>)
                {
                    var obj = entity as Dictionary<string, object>;
                    for (int i = 0; i < row.ItemArray.Length; i++)
                    {
                        var value = row[i];
                        if (value != DBNull.Value)
                        {
                            obj.Add(row.Table.Columns[i].ColumnName, value);
                        }
                    }
                }
                else
                {
                    for (int i = 0; i < row.ItemArray.Length; i++)
                    {
                        var attr = this.EntityMap.EntityFields.Where(x => x.Name == row.Table.Columns[i].ColumnName).FirstOrDefault();
                        if (attr != null)
                        {
                            var value = row[i];
                            if (value != DBNull.Value)
                            {
                                attr.SetValue(entity, value);
                            }
                        }
                    }
                }
            }
            if (entity != null && entity is EntityBase)
            {
                var entityClass = (entity as EntityBase);
                entityClass.IsUpdated = true;
                entityClass.UpdateField.Clear();
            }
            return entity;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="reader"></param>
        /// <returns></returns>
        public static T ReaderToEntity<T>(IDataReader reader) where T : new()
        {
            T entity = new T();
            if (reader != null)
            {
                if (entity is Dictionary<string, object>)
                {
                    var obj = entity as Dictionary<string, object>;
                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        var value = reader.GetValue(i);
                        obj.Add(reader.GetName(i), value != DBNull.Value ? value : null);
                    }
                }
     
                else
                {
                    var properties = entity.GetType().GetProperties();
                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        var attr = properties.Where(x => x.Name == reader.GetName(i)).FirstOrDefault();
                        if (attr != null)
                        {
                            var value = reader.GetValue(i);
                            if (value != DBNull.Value)
                            {

                                if (!string.IsNullOrWhiteSpace(attr.PropertyType.Name) && attr.PropertyType.Name.ToLower().Equals("boolean"))
                                {
                                    attr.SetValue(entity, (value.ToString().Equals("1")|| value.ToString().ToLower().Equals("true")));
                                }
                                else
                                {
                                    attr.SetValue(entity, value);
                                }
                                
                            }
                        }
                    }
                }
            }
            if (entity != null && entity is EntityBase)
            {
                var entityClass = (entity as EntityBase);
                entityClass.IsUpdated = true;
                entityClass.UpdateField.Clear();
            }
            return entity;
        }

        #endregion

        #region sqlstring
        public string GetSelectSql()
        {
            StringBuilder strsql = new StringBuilder(" select " + EntityMap.StrFieldBuilder + " from " + EntityMap.TableName + " where  ");
            if (EntityMap.Table.IsDeleted)
            {
                strsql.Append(EntityBase.__IsDeleted+"=0 ");
            }
            else
            {
                strsql.Append(" 1=1 " );
            }
            strsql.Append(" {0} ");
            //if (!isAll)
            //{
            //    strsql.Append(" and " + entitymap.PrimaryKey + "=" + this.GetParameter(entitymap.PrimaryKey));
            //}
            return strsql.ToString();

        }

        public string GetDeleteSql(ref DataParameterCollection parameters,bool isupdated = true)
        {
            StringBuilder strsql = new StringBuilder();
            if (!isupdated)
            {
                strsql.Append(" delete  from " + EntityMap.TableName);
            }
            else
            {
                strsql.Append(" update " + EntityMap.TableName + " set " + EntityBase.__IsDeleted + "=1 ");
                if (EntityMap.Table.IsLastModify)
                {
                    strsql.Append("," + EntityBase.__LastModifyDate + "=" + parameters.GetParameter(EntityBase.__LastModifyDate));
                    strsql.Append("," + EntityBase.__LastModifyUserId + "=" + parameters.GetParameter(EntityBase.__LastModifyUserId));
                    parameters.Add(EntityBase.__LastModifyDate,DateTime.Now);
                    parameters.Add(EntityBase.__LastModifyUserId, ApplicationEnvironments.DefaultSession.UserId);
                }
            }
            strsql.Append(" where 1=1 ");
            //if (isWhere)
            //{
            //    strsql.Append(" and " + entitymap.PrimaryKey + "=" + this.GetParameter(entitymap.PrimaryKey));
            //}

            strsql.Append(" {0} ");


            return strsql.ToString();
        }

        /// <summary>
        /// 生成update sql语句
        /// </summary>
        /// <param name="providerType"></param>
        /// <param name="entity"></param>
        /// <param name="where"></param>
        /// <param name="parameter"></param>
        /// <param name="strError"></param>
        /// <returns></returns>
        public string GetUpdate(IDbRepository dbRepository,object entity, ICriterion where, ref DataParameterCollection parameter, ref string strError)
        {
            if (parameter == null)
                parameter = new DataParameterCollection(dbRepository);
            StringBuilder strsql = new StringBuilder();

            strsql.Append(" update " + EntityMap.TableName + " set ");

            int index = 0;
            if (entity is EntityBase)
            {
                var baseEntity = entity as EntityBase;
                if (baseEntity.UpdateField == null || baseEntity.UpdateField.Count <= 0)
                {
                    strError = "没有需要更新的数据";
                    return "";
                }
                if (EntityMap.Table.IsLastModify)
                {
                    baseEntity.LastModifyDate = DateTime.Now;
                    baseEntity.LastModifyUserId = ApplicationEnvironments.DefaultSession.UserId;
                    if (!baseEntity.UpdateField.Contains(EntityBase.__LastModifyDate))
                    {
                        baseEntity.UpdateField.Add(EntityBase.__LastModifyDate);
                    }
                    if (!baseEntity.UpdateField.Contains(EntityBase.__LastModifyUserId))
                    {
                        baseEntity.UpdateField.Add(EntityBase.__LastModifyUserId);
                    }

                }

                foreach (var item in baseEntity.UpdateField)
                {
                    var dbField = EntityMap.DbFields.Where(x => x.Name.ToLower().Equals(item.ToLower())).FirstOrDefault();
                    if (dbField == null || !dbField.Update || !dbField.IsEnabled || dbField.IsPrimaryKey)
                    {
                        continue;
                    }
                    if (index > 0)
                    {
                        strsql.Append(",");
                    }
                    string parameterName = parameter.GetParameter(item);
                    strsql.Append(item + "=" + parameterName);
                    var attr = EntityMap.EntityFields.Where(x => x.Name.Equals(item)).FirstOrDefault();
                    object value = GetValue(entity, attr);
                    //if (!item.AllowDBNull && value == null)
                    //{
                    //    throw new Exception(item.Name + "不允许为空");
                    //}
                    index++;
   
                    parameter.Add(new DataParameter(item, (dbField.AllowDBNull&&value == null) ? DBNull.Value : value));
                }
            }
            else
            {
                foreach (var item in EntityMap.DbFields)
                {
                    if (!item.IsPrimaryKey && item.IsEnabled && item.Update)
                    {
                        if (index > 0)
                        {
                            strsql.Append(",");
                        }
                        string parameterName = parameter.GetParameter(item.Name);
                        strsql.Append(item.Name + "=" + parameterName);
                        var attr = EntityMap.EntityFields.Where(x => x.Name.Equals(item.Name)).FirstOrDefault();
                        object value = GetValue(entity, attr);
                        if (!item.AllowDBNull && value == null)
                        {
                            throw new Exception(item.Name + "不允许为空");
                        }
                        index++;
                      
                        parameter.Add(new DataParameter(item.Name, (item.AllowDBNull&&value == null) ? DBNull.Value : value));
                    }
                }
            }
            if (where == null)
            {
                strsql.Append(" where " + EntityMap.PrimaryKey + "=" + parameter.GetParameter(EntityMap.PrimaryKey));
                parameter.Add(new DataParameter(EntityMap.PrimaryKey, GetPrimaryId(entity)));
            }
            else
            {
                strsql.Append(" where " + where.ToString(ref parameter));
            }
         
            return strsql.ToString();
        }

        
        #endregion
    }
}
